Repository navigation
fix: harden server.json import review, URL elicitation scheme, and preview CSP injection - #2876
Merged
Merged
Conversation
The registry server.json import panel labelled each option by its package identifier only, so the command and arguments that would actually be saved and spawned (runtime arguments included) were never shown before Add Server. - The panel now shows the exact shell-quoted command line (or remote URL) and the env var names of the selected option, including when there is only one package. It is built from the same buildServerConfigForSelection output that submit persists. - A package whose identifier or version starts with "-" or contains whitespace is skipped like an unsupported package, since either would reach the runtime as a flag rather than as the package reference. Runtime arguments themselves are unchanged: registry runtimeArguments are legitimate (e.g. docker -v), so they are surfaced rather than filtered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
The URL elicitation modal passed the server-supplied URL to window.open without checking its scheme. It now uses the shared isHttpUrl allowlist: a non-http(s) URL disables Open in Browser and the panel says why, and the open handler re-checks before calling window.open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
wrapHtmlWithCsp located the first <head> with a regex, which can match a <head> inside a comment, script or attribute and splice the policy where the parser never applies it. The meta is now written as the first token (after a leading doctype, if any), so the parser always places it at the top of the head, ahead of any of the document's own markup. Full documents keep their own head and <html> attributes; the input is not wrapped in a fixed body. A Storybook play function asserts the parsed placement in a real browser, since happy-dom does not place a leading meta the way the HTML spec does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
A declared environment variable name is passed to docker run as `-e NAME` for OCI packages, so a name such as `--privileged` would be read as a docker flag. parseEnvVars now requires each name to match ^[A-Za-z_][A-Za-z0-9_]*$ (the registry schema types it as a bare string, so this is the POSIX shape). Behavior chosen: an invalid name rejects the whole package, the same way a flag-shaped identifier or version does, rather than dropping that one variable. Dropping it could silently remove a required variable and launch a misconfigured server; rejecting it leaves the document with the existing "No runnable package or remote" error when nothing else is runnable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
There was a problem hiding this comment.
🟡 Changes recommended
Critical issues allow external requests despite the preview CSP and let immediate submissions save runtime arguments that were never shown in the preview.
3 open issues
What changed in this PR
This PR addresses #2873 by hardening server.json imports, URL elicitation, and HTML previews across the shared core and web client.
Changes:
- Displays resolved launches before import and rejects unsafe package tokens and invalid environment-variable names.
- Restricts elicitation browser actions to HTTP(S), with visible refusal messages.
- Moves preview CSP insertion ahead of document markup and adds unit, integration, and browser regression coverage.
| File | Description |
|---|---|
core/mcp/import/serverJson.ts |
Validates package tokens and environment-variable names. |
clients/web/src/utils/commandLine.ts |
Adds shell-quoted command formatting. |
clients/web/src/utils/commandLine.test.ts |
Tests argument quoting and command formatting. |
clients/web/src/test/integration/mcp/import/serverJson.test.ts |
Covers invalid imports and preserved runtime flags. |
clients/web/src/hooks/useServerJsonImport.ts |
Builds resolved launch previews. |
clients/web/src/hooks/useServerJsonImport.test.tsx |
Tests command and remote previews. |
clients/web/src/components/groups/ServerImportJsonModal/ServerImportJsonModal.tsx |
Passes the preview into the import panel. |
clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.tsx |
Guards browser opening with HTTP(S) validation. |
clients/web/src/components/groups/PendingClientRequestModal/PendingClientRequestModal.test.tsx |
Tests refusal of unsupported and malformed URLs. |
clients/web/src/components/groups/ImportServerJsonPanel/ImportServerJsonPanel.tsx |
Displays commands, remote URLs, and environment names. |
clients/web/src/components/groups/ImportServerJsonPanel/ImportServerJsonPanel.test.tsx |
Tests launch-preview rendering. |
clients/web/src/components/groups/ImportServerJsonPanel/ImportServerJsonPanel.stories.tsx |
Adds preview data to import stories. |
clients/web/src/components/groups/ElicitationUrlPanel/ElicitationUrlPanel.tsx |
Displays refusal reasons and disables browser opening. |
clients/web/src/components/groups/ElicitationUrlPanel/ElicitationUrlPanel.test.tsx |
Tests refusal messaging and copy availability. |
clients/web/src/components/elements/ContentViewer/contentViewerUtils.ts |
Replaces head matching with leading CSP insertion. |
clients/web/src/components/elements/ContentViewer/contentViewerUtils.test.ts |
Covers markup, doctype, and fragment insertion cases. |
clients/web/src/components/elements/ContentViewer/ContentViewer.stories.tsx |
Adds a browser-parser regression for commented heads. |
Review effort: Balanced
Any doctype other than a plain <!DOCTYPE html> preceded by HTML whitespace now gets the meta written at the very start of the document. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
Add is disabled, and submit refuses, while the live text differs from the debounced text the launch preview was built from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
Member
Author
|
Copilot round 1 (review 5775536740): three findings, fixed in 87ab65c, ddc3bed and 521dd31 and answered in each thread.
|
Member
Author
|
Copilot review loop closed. Round 2 (review 5775895557) was clean: no inline comments, no open issues, and the three round-1 findings are marked resolved. |
This was referenced Nov 4, 2026
This was referenced Nov 4, 2026
[Vulnerability] modelcontextprotocol/inspector: Content Security Policy Bypass
Danyw24/mcp-radar#407
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Closes #2873
Fix for GHSA-rwv7-mh2c-ff2p, developed and reviewed in the advisory's private fork (inspector-GHSA-rwv7-mh2c-ff2p#1) and landed here as an ordinary PR. The commits are the fork branch's, unchanged.
Hardens three input-handling paths in the web client and the shared server.json import.
Fixes
core/mcp/import/serverJson.ts,useServerJsonImport.ts,ImportServerJsonPanel.tsx)buildServerConfigForSelectionoutput thatsubmitpersists, so the preview can't drift from what gets saved.identifierorversionstarts with-or contains whitespace is skipped the same way an unsupported package is. A document with no other runnable option gets the existing "No runnable package or remote" error.runtimeArgumentsare deliberately left unfiltered. Registry runtime flags are legitimate (for example docker-v), so the fix makes them visible.clients/web/src/utils/commandLine.ts(quoteShellArg,formatCommandLine).PendingClientRequestModal.tsx,ElicitationUrlPanel.tsx)isHttpUrlfromlib/downloadFile.ts.window.open. Copy URL stays enabled.core/mcp/import/serverJson.tsparseEnvVars)^[A-Za-z_][A-Za-z0-9_]*$. The registry schema typesnameas a bare string, so this is the POSIX shape. Without the check, a name like--privilegedreaches docker as-e --privileged.contentViewerUtils.ts)wrapHtmlWithCspno longer searches for<head>with a regex. It writes the meta as the document's first token, after a leading doctype if there is one, so the HTML parser always places it at the top of the head.<html>attributes. The input is not wrapped in a fixed<body>.Tests
serverJson.test.ts:-vruntime argument is preserved--privilegedas an env var name is skipped, falling back to the next option1ABC,A B,A=B,-x) is rejected-e NAMEcommandLine.test.ts: quoting rules.useServerJsonImport.test.tsx:describeLaunchfor stdio and remote configslaunchPreviewfor a single package with a-cruntime argumentImportServerJsonPanel.test.tsx: the command, URL and absent-preview cases.ElicitationUrlPanel.test.tsxandPendingClientRequestModal.test.tsx:javascript:,data:,file:and unparseable URLs are refused, the button is disabled, the reason is shown, andwindow.openis never called.contentViewerUtils.test.ts:<!-- <head> -->before the real head<head>inside a script or attributeContentViewer.stories.tsxHtmlCommentedHead: a play function parses the wrapped<!-- <head> -->document in real Chromium. It asserts that the CSP meta is the head's first element and that the title,langand body survive. happy-dom doesn't place a leading meta per the HTML spec, so this can't be asserted in the unit project.npm run local:gateis green on this branch.🤖 Generated with Claude Code